package com.qianxun.plugins.shiro;

import com.qianxun.framework.common.util.Md5Util;
import lombok.Data;
import lombok.experimental.Accessors;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.apache.shiro.realm.AuthorizingRealm;

/**
 * 抽象realm，用于提取realm中相同功能部分，以复用
 * @author: huwei
 * @date: 2019/11/28 13:58
 * @version: 1.0.0
 */
public abstract class AbstractRealm  extends AuthorizingRealm {

    private SimpleCredentialsMatcher simpleCredentialsMatcher;

    public AbstractRealm(){
        this.simpleCredentialsMatcher = new PasswordMd5CredentialsMatcher();

        //设置默认凭证比对器
        setCredentialsMatcher(this.simpleCredentialsMatcher);
    }

    /**
     * 登录认证，验证用户登录的密码
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
            throws AuthenticationException {

        beforeDoGetAuthenticationInfo(token);

        UserInfo userInfo = getUserInfo(token);
        //用户不存在
        if(!userInfo.isExistUser()){
            UnknownAccountException unknownAccountException = new UnknownAccountException("用户不存在");
            errorDoGetAuthenticationInfo(token ,unknownAccountException);
            throw unknownAccountException;
        }

        //用户被锁定
        if(userInfo.isLockedUser()){
            LockedAccountException lockedAccountException = new LockedAccountException("账户已被锁定");
            errorDoGetAuthenticationInfo(token ,lockedAccountException);
            throw lockedAccountException;
        }

        afterDoGetAuthenticationInfo(token);

        return userInfo.getAuthenticationInfo();
    }

    /**
     * 获取用户信息
     * @return
     */
    protected abstract UserInfo getUserInfo(AuthenticationToken token);

    /**
     * doGetAuthenticationInfo(AuthenticationToken token) 方法之前执行
     * @param token
     */
    protected void beforeDoGetAuthenticationInfo(AuthenticationToken token){}

    /**
     * doGetAuthenticationInfo(AuthenticationToken token) 方法异常时执行
     * @param token
     * @param throwable
     */
    protected void errorDoGetAuthenticationInfo(AuthenticationToken token ,Throwable throwable){}

    /**
     * doGetAuthenticationInfo(AuthenticationToken token) 方法之后执行
     * @param token
     */
    protected void afterDoGetAuthenticationInfo(AuthenticationToken token){}

    protected SimpleCredentialsMatcher getSimpleCredentialsMatcher(){
        return this.simpleCredentialsMatcher;
    }

    /**
     * 密码md5校验
     * @author: huwei
     * @date:   2019/11/28 14:09
     * @version:  1.0.0
    */
    public static class PasswordMd5CredentialsMatcher extends SimpleCredentialsMatcher {
        @Override
        public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
            LoginTypeToken customShiroToken = (LoginTypeToken) token;
            char[] passwordChars = customShiroToken.getPassword();
            String oldPassword = info.getCredentials().toString();
            boolean result = Md5Util.getInstance().saltValid(new String(passwordChars) ,oldPassword);

            if(!result){
                //密码不匹配
                throw new AuthenticationException("密码不匹配");
            }
            return true;
        }
    }

    /**
     * 用户信息
     * @author: huwei
     * @date:   2019/11/28 14:49
     * @version:  1.0.0
    */
    @Data
    @Accessors(chain = true)
    final public static class UserInfo{
        private boolean existUser;
        private boolean lockedUser;
        private AuthenticationInfo authenticationInfo;

        public UserInfo(boolean existUser, boolean lockedUser, AuthenticationInfo authenticationInfo) {
            this.existUser = existUser;
            this.lockedUser = lockedUser;
            this.authenticationInfo = authenticationInfo;
        }
    }
}
